#include "jse_env.h"
#include "jse_log.h"
#include "module_init.h"
#include "module_glue.h"
#include <spdlog/logger.h>

#include <uvw.hpp>
#include <cassert>

javascript_environment::~javascript_environment()
{
}

bool javascript_environment::init_jse()
{
    try {
        rt_ = std::make_shared<qjs::Runtime>();
        ctx_ = std::make_shared<qjs::Context>(*rt_, [](JSRuntime* rt){
            //使用自定义的初始化函数
            auto ctx = jse_custom_context(rt);
            js_init_module_std(ctx, "std");
            //加载内置js模块
            jse_load_modules(ctx);
            return ctx;
        });
        //绑定导出类
        auto& native_module = ctx_->addModule("@engine");
        qjs_glue(native_module);



    }catch(...) {
        return false;
    }
    return true;
}

bool javascript_environment::init_uv()
{
    loop_ = uvw::Loop::create();
    prepare_ = loop_->resource<uvw::PrepareHandle>();
    prepare_->on<uvw::PrepareEvent>([](const uvw::PrepareEvent &evt, uvw::PrepareHandle &h) {
        // printf("prepare\n");
    });
    prepare_->start();
    
    check_ = loop_->resource<uvw::CheckHandle>();
    check_->on<uvw::CheckEvent>([](const uvw::CheckEvent &evt, uvw::CheckHandle &h) {
        // printf("check\n");
    });
    check_->start();

    idle_ = loop_->resource<uvw::IdleHandle>();
    idle_->on<uvw::IdleEvent>([this](const uvw::IdleEvent &evt, uvw::IdleHandle &h) {
        //调度任务
        do {
            decltype(async_calls_) tmp;
            {
                std::lock_guard<std::mutex> lck(mtx_async_calls_);
                if(async_calls_.empty())
                    break;
                std::swap(async_calls_, tmp);
            }
            //依次执行主线程推过来的调用请求
            for(auto& cb : tmp) {
                try {
                    auto result = (*cb)();
                    cb->result(result);
                }catch (const std::exception& e) {
                    cb->execption(std::current_exception());
                }catch(const qjs::exception& e) {
                    //qjs exception，获取并传递js exception
                    auto v = ctx_->getException();
                    if((bool)v["stack"]) {
                        LOG_ERROR("stack trace:\n{}",(std::string)v["stack"]);
                    }
                    cb->result(v);
                }
            }
            tmp.clear();
        }while(0);
    });
    idle_->start();

    quit_ = loop_->resource<uvw::AsyncHandle>();
    quit_->on<uvw::AsyncEvent>([this](const uvw::AsyncEvent &evt, uvw::AsyncHandle &h) {
        h.loop().stop();
    });

    return true;
}

bool javascript_environment::init()
{
    bool bOk = true;
    if(!init_jse()) {
        return false;
    }
    if(!init_uv()) {
        return false;
    }


    return true;
}

bool javascript_environment::exec()
{
    bool ret = loop_->run();
    loop_->close();
    return ret;
}

void javascript_environment::stop()
{
    quit_->send();
}

std::shared_ptr<qjs::Context> javascript_environment::get_context()
{
    return ctx_;
}

qjs::Value javascript_environment::eval_javascript_string(const std::string& javascript)
{
    auto&& act = execute_async_call([](const std::shared_ptr<javascript_environment>& obj, const std::string& javascript){
        auto ret = obj->ctx_->eval(javascript, "jse_native", JS_EVAL_TYPE_MODULE);
        return ret;
    }, shared_from_this(), javascript);
    return act->result();
}

qjs::Value javascript_environment::eval_javascript_file(const std::string &jsfilepath)
{
    auto&& act = execute_async_call([](const std::shared_ptr<javascript_environment>& obj, const std::string& jsfilepath){
        auto ret = obj->ctx_->evalFile(jsfilepath.c_str());
        return ret;
    }, shared_from_this(), jsfilepath);
    return act->result();
}

void javascript_environment::set_init_result(bool result)
{
    init_result_.set_value(result);
}

bool javascript_environment::get_init_result()
{
    return init_result_.get_future().get();
}